# -*- coding: utf-8 -*-

import os
import sys
from contextlib import closing
import time
import hashlib
import simplejson
import jinja2
import httplib, urllib
import threading
import logging
import werkzeug.urls
import datetime

import openerp
from openerp import http
from openerp.http import request
from openerp.modules.registry import RegistryManager
from openerp.api import Environment

from openerp.addons.wechat.official import WxApi, WxApplication, WxTextResponse

from openerp.addons.website_exchange.controllers.main import WebsiteExchange

SUPERUSER_ID = 1

_logger = logging.getLogger(__name__)

#微信服务号提货处理类
class WxOfficalExchange(WxApplication):
    default_interval = 7200 #7200秒，2个小时

    def __init__(self):
        self.wxapi = WxApi(self.APP_ID, self.SECRET)

        #取得微信应用配置
        db_name = request.session.db
        db = RegistryManager.get(db_name)
        ir_model_data = openerp.registry(db_name)['ir.model.data']
        with closing(db.cursor()) as cr:
            self.official_id = ir_model_data.xmlid_to_res_id(cr, 1, 'dzx_weixin.weixin_official_dzx')
            wechat_official = openerp.registry(db_name)['wechat.official']
            official = wechat_official.browse(cr, openerp.SUPERUSER_ID, self.official_id)
            if official:
                self.APP_ID = official.app_id.encode("ascii")
                self.SECRET = official.secret
                self.TOKEN = official.token

                interval = 0
                remainder_interval = int(time.time()) - official.access_token_timestamp
                #如果之前已经保存了access_token而且没有超过7200秒，则可以继续使用
                if official.access_token and remainder_interval < self.default_interval:
                    self.access_token = official.access_token
                    self.jsapi_ticket = official.jsapi_ticket
                    #下次更新时间间隔
                    interval = self.default_interval - remainder_interval
                else:
                    self.request_sh715_token()
                    self.request_sh715_ticket()

                if openerp.tools.config.get('is_weixin_server'):
                    threaded_http = threading.Thread(target=self.loop, args=(db, interval))
                    threaded_http.daemon = True
                    threaded_http.start()






    #发送文本回调
    def on_text(self, req):
        #原样返回
        return WxTextResponse(req.Content, req)

    #后台线程获取access_token
    def loop(self, registry, interval, context=None):
        while True:
            try:
                if interval > 0:
                    time.sleep(interval)
                    interval = 0
                else:
                    time.sleep(self.default_interval)
                self.request_weixin(registry, context)
            except Exception, e:
                _logger.exception("weixin get token loop error, sleep and retry")
                time.sleep(5)

    #向微信服务器获取access_token
    def request_weixin(self, registry, context=None):
        with Environment.manage():
            http_client = None
            try:
                data = {'grant_type': 'client_credential', 'appid': self.APP_ID, 'secret': self.SECRET}
                params = urllib.urlencode(data)

                headers = {"Content-type": "application/x-www-form-urlencoded",
                           "Accept": "text/plain"}
                http_client = httplib.HTTPSConnection("api.weixin.qq.com", timeout=5)
                http_client.request("POST", "/cgi-bin/token", params, headers)

                response = http_client.getresponse()
                if response.status == 200:
                    data_string = response.read()
                    print '---------- weixin get access tocket:', data_string
                    #{"access_token":"ACCESS_TOKEN","expires_in":7200}
                    return_data = simplejson.loads(data_string)
                    if return_data.get('errcode'):
                        _logger.error('get weixin access_token error: %s',  data_string)
                    else:
                        self.access_token = return_data['access_token']

                        data = {'access_token': self.access_token, 'type': 'jsapi'}
                        params = urllib.urlencode(data)
                        http_client.request("POST", "/cgi-bin/ticket/getticket", params, headers)
                        response = http_client.getresponse()
                        if response.status == 200:
                            data_string = response.read()
                            print '---------- weixin get jsapi ticket:', data_string
                            return_data = simplejson.loads(data_string)
                            if return_data.get('errmsg') != 'ok':
                                _logger.error('get weixin jsapi ticket error: %s',  data_string)
                            else:
                                self.jsapi_ticket = return_data['ticket']
                                wechat_official = registry['wechat.official']
                                with closing(registry.cursor()) as cr:
                                    official_row = wechat_official.browse(cr, openerp.SUPERUSER_ID, self.official_id)
                                    timestamp = int(time.time())
                                    official_row.write({'access_token': self.access_token,
                                                        'access_token_timestamp': timestamp,
                                                        'jsapi_ticket': self.jsapi_ticket,
                                                        'jsapi_ticket_timestamp': timestamp})
                                    cr.commit()

            finally:
                if http_client:
                    http_client.close()


    #向大闸蟹sh715中间服务器获取token
    def request_sh715_token(self, context=None):
        http_client = None
        try:
            http_client = httplib.HTTPConnection("wx.ychp.cn", timeout=5)
            http_client.request("GET", "/dazhaxie/token/?act=token")

            response = http_client.getresponse()
            if response.status == 200:
                self.access_token = response.read()
                return self.access_token
        finally:
            if http_client:
                http_client.close()

    #向大闸蟹sh715中间服务器获取ticket
    def request_sh715_ticket(self, context=None):
        http_client = None
        try:
            http_client = httplib.HTTPConnection("wx.ychp.cn", timeout=5)
            http_client.request("GET", "/dazhaxie/token/?act=ticket")

            response = http_client.getresponse()
            if response.status == 200:
                self.jsapi_ticket = response.read()
                return self.jsapi_ticket
        finally:
            if http_client:
                http_client.close()

#####################################################################
#
# 企业服务号应用请求处理
#
#####################################################################
class Weixin_Offical(http.Controller):

    def __init__(self):
        self.weixin_official = WxOfficalExchange()

    #微信请求处理入口
    @http.route('/wx/official', auth='public')
    def weixin_official(self, **kw):
        result = self.weixin_official.process(request.httprequest.args, request.httprequest.data)
        return result

    #微信卡券code解码
    def _weixin_code_decrypt_return_data(self, encrypt_code):
        http_client = None
        data = '{}'
        try:
            data = {'encrypt_code': encrypt_code}
            params = simplejson.dumps(data)

            headers = {"Content-type": "application/x-www-form-urlencoded",
                       "Accept": "text/plain"}
            http_client = httplib.HTTPSConnection("api.weixin.qq.com", timeout=5)
            access_token = self.weixin_official.access_token
            http_client.request("POST", "/card/code/decrypt?access_token=%s" % access_token, params, headers)

            response = http_client.getresponse()
            if response.status == 200:
                data_string = response.read()
                #{"access_token":"ACCESS_TOKEN","expires_in":7200}
                data = simplejson.loads(data_string)
        finally:
            if http_client:
                http_client.close()

        return data

    #微信卡券code解码
    def _weixin_code_decrypt_return_code(self, encrypt_code):
        data = self._weixin_code_decrypt_return_data(encrypt_code)
        if data.get('errcode'):
            logging.error('weixin errcode: %s' % data.get('errcode'))
            time.sleep(2)
            self.weixin_official.request_sh715_token()
            data = self._weixin_code_decrypt_return_data(encrypt_code)

        if data.get('errcode') == 0:
            return data['code']
        else:
            _logger.error('weixin card decrypt return errcode: %s' % data.get('errcode'))

    #微信卡券核销
    @http.route('/card/consume/<string:code>/<string:card_id>', auth='user')
    def _weixin_code_consume(self, code, card_id):
        http_client = None
        data = '{}'
        try:
            data = {'code': code, 'card_id': card_id}
            params = simplejson.dumps(data)

            headers = {"Content-type": "application/x-www-form-urlencoded",
                       "Accept": "text/plain"}
            http_client = httplib.HTTPSConnection("api.weixin.qq.com", timeout=5)
            access_token = self.weixin_official.access_token
            http_client.request("POST", "/card/code/consume?access_token=%s" % access_token, params, headers)

            response = http_client.getresponse()
            if response.status == 200:
                data_string = response.read()
                print u'---------- 卡券核销:', data_string
                data = simplejson.loads(data_string)
                #如果access_token过期, 则更新后再次调用核销
                if data.get('errcode') == 42001:
                    self.weixin_official.request_sh715_token()
                    data = self._weixin_code_consume(code, card_id)

        finally:
            if http_client:
                http_client.close()

        return data

    # 微信提货入口(外部网址查询http://th.ychp.cn/wxth?openid=1111111
    # 外部网址提货http://test.localhost/wxth?card_id=ptInVjpqRRChM-I79NlYWiswbIF8&encrypt_code=EiUcvZ7aOP%2FUSNq5WX%2B%2FTJD30pMRhHwrjHllxwSvO%2B4%3D&signature=e5d4cfb34b2bd5d3fcd0f646612a9d6fedd9e337&openid=otInVjgEcSL1RFU8UqOt5n9RFpuw
    # 外部网址由nginx转发到这个路由
    @http.route('/wxth', auth='public')
    def weixin_card_exchange(self, **kw):
        openid = kw.get('openid')
        encrypt_code = kw.get('encrypt_code')
        card_id = kw.get('card_id')
        signature = kw.get('signature')
        registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
        #如果用户点击微信卡包内的卡券提货链接
        if encrypt_code and card_id:
            value_list = [signature, encrypt_code, card_id]
            value_list.sort()
            #comp_signature = hashlib.sha1("".join(value_list)).hexdigest()
            code = self._weixin_code_decrypt_return_code(encrypt_code)
            if code:
                # 由于之前微信测试的电子卡券券号没有重视，跟纸质卡券重复了
                # 这部分电子卡券的特征是product.card.line表的is_electron不为true(微信电子卡券导出后会自动设置is_electron为true)
                # 在1元测试产品下发行这些测试卡券号码，前缀增加“E”已区分
                # 从微信卡包提货链接过来的卡券号码，如果检查后不是电子卡券，那么增加前缀“E”
                product_card_obj = registry.get('product.card.line')
                code_id = product_card_obj.search(cr, SUPERUSER_ID, [('code', '=', code)])
                if code_id:
                    code_row = product_card_obj.read(cr, SUPERUSER_ID, code_id)[0]
                    if not code_row['is_electron']:
                        code = 'E' + code
                    #卡券号码放入session
                    request.session.card_code = code
                    #跳转到提货页面
                    return werkzeug.utils.redirect('/card/exchange/new?card_id=%s' % (card_id,))
                else:
                    # 没有找到卡号的时候，尝试查找E开头的"伪电子券"
                    code = 'E' + code
                    code_id = product_card_obj.search(cr, SUPERUSER_ID, [('code', '=', code)])
                    if code_id:
                        request.session.card_code = code
                        # 跳转到提货页面
                        return werkzeug.utils.redirect('/card/exchange/new?card_id=%s' % (card_id,))
        else:
            _logger.warn('weixin card encrypt return warn: signature:%s, encrypt_code:%s, card_id:%s' % (signature, encrypt_code, card_id))

        if openid:
            request.session['openid'] = openid
            #跳转到提货或查询首页
            return werkzeug.utils.redirect('/card/exchange/index')


    # 微信查询入口(外部网址查询http://th.ychp.cn/wxcx?openid=1111111
    @http.route('/wxcx', auth='public')
    def weixin_card_exchange_query(self, **kw):
        openid = kw.get('openid')
        if openid:
            request.session['openid'] = openid
            #跳转到提货或查询首页
            return werkzeug.utils.redirect('/card/exchange/index?chaxun=1')

    #卡券激活接口，成功返回OK，错误返回错误信息
    #需要sha1签名
    @http.route('/wx/card/activate', auth='public')
    def weixin_card_active(self, **kw):
        registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
        cards = kw.get('cards')
        signature = kw.get('signature')
        #计算签名
        comp_signature = hashlib.sha1(cards + 'yangchenghupaidazhaxie_active_secret').hexdigest()
        if not cards:
            return u'缺少逗号分隔的卡券编号参数'
        if comp_signature == signature:
            card_list = cards.split(',')
            product_card_obj = registry.get('product.card.line')
            # 找到对应的卡券IDS
            card_ids = product_card_obj.search(cr, SUPERUSER_ID, [('code', 'in', card_list)])

            # 激活卡券
            #message = product_card_obj.do_active(cr, SUPERUSER_ID, card_ids)

            if message:
                return message
            else:
                return 'OK'
        else:
            return u'SHA1签名错误'


    @http.route('/wx/card/exchange', auth='public', website=True)
    def weixin_card_exchange_save(self, **kw):
        registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
        #如果提交了兑换请求
        if kw.get('submit') == 'exchange':
            message = WebsiteExchange.chard_exchange_info_check(kw)
            if message:
                #如果有错误,重现进入表单页面
                return self.card_exchange_new(kw, request.website)
            else:
                result = WebsiteExchange.card_exchange_save(kw)
                #兑换成功，则核销
                if result:
                    card_id = kw.get('card_id')
                    card_code = request.session.card_code
                    data = self._weixin_code_consume(card_code, card_id)
                    if data.get('errcode') == 0:
                        #写入openid
                        openid = data.get('openid')
                        apply_delivery = registry.get('apply.delivery')
                        exchange_row = apply_delivery.browse(cr, SUPERUSER_ID, result)
                        exchange_row.write({'openid': openid})
                        return werkzeug.utils.redirect('/card/exchange/info' + '?exchange_ok=1')
                    else:
                        request.cr.rollback()
                        return werkzeug.utils.redirect('/card/exchange/info')
                else:
                    #如果不成功, 重新进入表单页面
                    return self.card_exchange_new(kw, request.website)

        return werkzeug.utils.redirect('/card/exchange/info')


    def card_exchange_new(self, kw, website):
        registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
        #website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context)
        #website = website.with_context(request.context)
        values = kw
        message = []

        #起始发货日期
        config_start_delivery_date_string = registry.get('ir.config_parameter')\
                                                .get_param(cr, uid, 'estimated_ship_start_date') or '2015-09-01'
        #结束发货日期
        config_end_delivery_date_string = registry.get('ir.config_parameter')\
                                                .get_param(cr, uid, 'estimated_ship_end_date') or '2016-02-01'

        config_start_delivery_date = datetime.datetime.strptime(config_start_delivery_date_string +\
                                                                ' 00:00:00', '%Y-%m-%d %H:%M:%S')
        #config_start_delivery_date = config_start_delivery_date.replace(tzinfo=pytz.utc).astimezone(pytz.timezone('Asia/Shanghai'))

        config_end_delivery_date = datetime.datetime.strptime(config_end_delivery_date_string +\
                                                              ' 23:59:59', '%Y-%m-%d %H:%M:%S')
        #config_end_delivery_date = config_end_delivery_date.replace(tzinfo=pytz.utc).astimezone(pytz.timezone('Asia/Shanghai'))

        now = datetime.datetime.now()
        #如果提货起始时间比当天后的第三天晚，则从提货起始时间开始，否则起始选择时间为当第三天 (原需求)
        #4点之前可以第二天提货，4点之后可以后天提货 (2015-10-12需求)
        #改成原来的(2015-10-16龚献平需求)
        #now_hour = int((now + datetime.timedelta(hours=8)).strftime('%H'))
        #start_delivery_date = now + datetime.timedelta(hours=8) + datetime.timedelta(days=1)

        #当天16点之前48小时后，16点之后72小时（苏定华2017-06-01需求）
        now_hour = int((now + datetime.timedelta(hours=8)).strftime('%H'))
        start_delivery_date = now + datetime.timedelta(hours=8) + datetime.timedelta(days=2)
        if now_hour >= 16:
            start_delivery_date = now + datetime.timedelta(hours=8) + datetime.timedelta(days=3)
        start_delivery_date = datetime.datetime(start_delivery_date.year, start_delivery_date.month, start_delivery_date.day, 00, 00, 00)
        if config_start_delivery_date > start_delivery_date:
            start_delivery_date = config_start_delivery_date

        #如果提交了兑换请求
        if kw.get('submit') == 'exchange':
            #检查提交内容
            if not values.get('to_name'):
                message.append(u'请输入收货人姓名')
            if not values.get('mobile_number') and not values.get('phone_number'):
                message.append(u'请填写手机号码或者电话号码至少一个')
            if not values.get('delivery_date'):
                message.append(u'请选择期望发货日期')
            if not values.get('province'):
                message.append(u'请选择省市')
            if not values.get('city'):
                message.append(u'请选择市')
            if not values.get('county'):
                message.append(u'请选县区')
            if not values.get('address'):
                message.append(u'请输入街道门牌地址')
            else:
                try:
                    input_delivery_date = datetime.datetime.strptime(values.get('delivery_date') +\
                                                                ' 07:07:07', '%Y-%m-%d %H:%M:%S')
                    #input_delivery_date = input_delivery_date.replace(tzinfo=pytz.utc).astimezone(pytz.timezone('Asia/Shanghai'))
                    #如果早于允许的起始选择时间，则提示错误
                    if input_delivery_date < start_delivery_date:
                        message.append(u'选择的期望发货日期不能早于' + start_delivery_date.strftime('%Y-%m-%d'))
                    if input_delivery_date > config_end_delivery_date:
                        message.append(u'选择的期望发货日期不能晚于' + config_end_delivery_date_string)
                except ValueError as ve:
                    message.append(u'日期格式不正确, 需要减号隔开年月日，如: 2016-01-01')

            if not message:
                result = WebsiteExchange.card_exchange_save(kw)
                if result:
                    return werkzeug.utils.redirect('/card/exchange/info' + '?exchange_ok=1')
                else:
                    message.append(u'卡券提货记录保存失败，请联系客服')

        #从session中取得卡号
        card_code = request.session.card_code

        #取得省份列表
        cr.execute('SELECT rcs.id, rcs.name FROM res_country_state rcs '\
                   'INNER JOIN res_country rc ON rc.id=rcs.country_id '\
                   'WHERE rc.name=%s', ('China',))
        provinces = cr.dictfetchall()
        #省份列表按中文读音排序
        provinces_sorted = provinces

        #如果有选择省份，则取得城市列表
        values['cities'] = []
        if kw.get('province'):
            cr.execute('SELECT id, city_name as name FROM all_city WHERE province_name=%s', (kw.get('province'),))
            values['cities'] = cr.dictfetchall()
            values['cities'] = values['cities']
        #如果有选择城市，则取得市县列表
        values['counties'] = []
        if kw.get('city'):
            cr.execute('SELECT id, county_name as name FROM all_country WHERE city_name=%s', (kw.get('city'),))
            values['counties'] = cr.dictfetchall()
            values['counties'] = values['counties']

        #如果微信电子卡券提货，需要微信电子卡券核销，也就是form需要提交到微信提货模块去处理
        #values['form_action'] = '/card/exchange/new'
        values['form_action'] = '/wx/card/exchange'
        if kw.get('card_id'):
            values['form_action'] = '/wx/card/exchange'
            #进行激活操作
            product_card_obj = registry.get('product.card.line')
            # 找到对应的卡券IDS
            card_ids = product_card_obj.search(cr, SUPERUSER_ID, [('code', 'in', [card_code])])
            if card_ids:
                card = product_card_obj.browse(cr, SUPERUSER_ID, card_ids[0])
                if not card.is_active:
                    # 激活卡券
                    # product_card_obj = registry.get('product.card.line')
                    # try:
                    #     product_card_obj.do_active(cr, SUPERUSER_ID, card_ids)
                    # except openerp.exceptions.except_orm as e:
                    #     cr.rollback()
                    #     _logger.error(u'微信电子卡券激活失败: %s' % e[1])
                    message.append(u'卡券没有激活，请联系客服')

        #取得卡券对应的产品名称
        cr.execute('SELECT id, is_active, is_enabled, is_exchanged, product_id FROM product_card_line WHERE code=%s',
                   (card_code,))
        card = cr.dictfetchone()
        product_name = ''
        if card:
            cr.execute('SELECT name_template FROM product_product WHERE id=%s', (card['product_id'],))
            product_name = cr.dictfetchone()['name_template']

        #values['debug'] = True
        #设置发日期默认值
        if not values.get('delivery_date'):
            values['delivery_date'] = start_delivery_date.strftime('%Y-%m-%d')
        values['message'] = "<br/>".join(message)
        values['card'] = card
        values['product_name'] = product_name
        values['provinces'] = provinces_sorted
        values['start_delivery_date'] = start_delivery_date.strftime('%Y-%m-%d')
        values['end_delivery_date'] = config_end_delivery_date.strftime('%Y-%m-%d')
        return website.render('website_exchange.new', values)